梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)
本教程将从面向对象的核心概念出发,系统讲解C++类的定义、实现、对象实例化,以及面向对象三大特性中的继承和多态,帮助你掌握C++面向对象编程的核心思想和实践技巧。
编程范式是编写程序的方法论,C++支持面向过程和面向对象两种核心范式,二者的核心思想存在本质差异:
| 维度 | 面向过程(Procedure-Oriented) | 面向对象(Object-Oriented) |
|---|---|---|
| 核心思想 | 以“过程”为中心,将程序拆解为函数的集合 | 以“对象”为中心,将程序拆解为对象的交互 |
| 设计思路 | 关注“怎么做”,按步骤实现功能 | 关注“谁来做”,将数据和行为封装给对象 |
| 数据与函数 | 数据和函数分离,函数操作外部数据 | 数据和函数封装为一体,仅通过接口访问 |
| 扩展性 | 扩展困难,修改需改动大量代码 | 易扩展,通过继承/多态实现复用和扩展 |
| 典型应用 | 小型工具、简单算法(如计算器、排序) | 大型软件、复杂系统(如游戏、框架) |
类是C++面向对象编程的核心载体,是对一类具有相同属性和行为的事物的抽象描述:
对象是类的实例化产物,是类模板创建的具体实体:
示例类比:
类 = 汽车设计图纸(定义了汽车的属性:颜色、排量;行为:启动、刹车)
对象 = 具体的一辆宝马车、一辆奔驰车(拥有自己的颜色/排量,可执行启动/刹车操作)
成员变量是类的“数据部分”,用于描述对象的状态和特征,也称为属性或数据成员:
成员函数是类的“行为部分”,用于操作成员变量,实现对象的功能,也称为方法或成员方法:
C++通过访问权限修饰符控制类成员的可见性,实现封装性,核心包含三种权限:
| 权限修饰符 | 类内访问 | 类外访问 | 子类访问 |
|---|---|---|---|
| public(公有) | √ | √ | √ |
| protected(保护) | √ | × | √ |
| private(私有) | √ | × | × |
注意:
类的声明使用class关键字,语法格式如下:
class 类名 {
// 访问权限修饰符(默认private)
public:
// 公有成员变量/函数(对外接口)
protected:
// 保护成员变量/函数(子类可访问)
private:
// 私有成员变量/函数(仅类内可访问)
}; // 注意分号结尾
class Person {
// 私有成员(默认)
char m_name[20]; // 姓名
int m_age; // 年龄
float m_height; // 身高
public:
// 公有成员函数(对外接口)
void setInfo(const char* name, int age, float height); // 设置信息
void showInfo(); // 显示信息
int getAge(); // 获取年龄
void setAge(int age); // 设置年龄
};
成员函数直接写在类体内,自动成为内联函数(适合简单函数):
class Person {
private:
int m_age;
public:
// 类内实现(内联函数)
int getAge() {
return m_age;
}
};
成员函数在类外实现,需使用类名::函数名指定作用域:
// 类的声明
class Person {
private:
char m_name[20];
int m_age;
float m_height;
public:
void setInfo(const char* name, int age, float height);
void showInfo();
};
// 类外实现成员函数
void Person::setInfo(const char* name, int age, float height) {
strcpy(m_name, name);
m_age = age;
m_height = height;
}
void Person::showInfo() {
cout << "姓名:" << m_name << endl;
cout << "年龄:" << m_age << endl;
cout << "身高:" << m_height << "m" << endl;
}
构造函数和析构函数是类的特殊成员函数,负责对象的初始化和资源释放:
用于对象创建时初始化成员变量,特点:
class Person {
private:
char m_name[20];
int m_age;
public:
// 无参构造函数
Person() {
strcpy(m_name, "未知");
m_age = 0;
cout << "无参构造函数调用" << endl;
}
// 有参构造函数(重载)
Person(const char* name, int age) {
strcpy(m_name, name);
m_age = age;
cout << "有参构造函数调用" << endl;
}
void showInfo() {
cout << "姓名:" << m_name << " 年龄:" << m_age << endl;
}
};
用于对象销毁时释放资源(如堆内存、文件句柄),特点:
class Person {
private:
char* m_name; // 堆区字符串
public:
// 构造函数:分配堆内存
Person(const char* name) {
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
// 析构函数:释放堆内存
~Person() {
delete[] m_name;
m_name = nullptr;
cout << "析构函数:释放内存" << endl;
}
};
C++中对象的创建主要有三种方式,对应不同的内存区域:
int main() {
// 栈区创建对象,自动调用构造函数
Person p1; // 无参构造
Person p2("张三", 18); // 有参构造
// 栈区对象销毁时自动调用析构函数
return 0;
}
int main() {
// 堆区创建对象,返回指针
Person* p3 = new Person(); // 无参构造
Person* p4 = new Person("李四", 20); // 有参构造
// 手动释放堆区对象(否则内存泄漏)
delete p3;
delete p4;
p3 = nullptr;
p4 = nullptr;
return 0;
}
int main() {
// 静态对象,程序结束时才销毁
static Person p5("王五", 22);
return 0;
}
根据对象类型,使用不同的访问运算符:
Person p("赵六", 19);
p.showInfo(); // 调用成员函数
p.setAge(20); // 修改私有成员(通过公有接口)
Person* p = new Person("孙七", 21);
p->showInfo(); // 调用成员函数
p->setAge(22); // 修改私有成员
delete p;
继承是面向对象三大特性之一,允许新类(子类/派生类)复用旧类(父类/基类)的属性和方法,实现代码复用和扩展。
// 基类(父类)
class Person {
protected:
char m_name[20];
int m_age;
public:
Person(const char* name, int age) {
strcpy(m_name, name);
m_age = age;
}
void showBaseInfo() {
cout << "姓名:" << m_name << " 年龄:" << m_age << endl;
}
};
// 派生类(子类):继承Person
class Student : public Person {
private:
float m_score; // 新增成员
public:
// 子类构造函数:必须调用父类构造函数初始化父类成员
Student(const char* name, int age, float score) : Person(name, age) {
m_score = score;
}
// 新增成员函数
void showStudentInfo() {
showBaseInfo(); // 调用父类成员函数
cout << "成绩:" << m_score << endl;
}
};
// 测试继承
int main() {
Student stu("周八", 18, 92.5);
stu.showStudentInfo(); // 子类对象可调用自身和父类的公有/保护成员
return 0;
}
继承方式决定了父类成员在子类中的访问权限:
| 父类权限 | public继承 | protected继承 | private继承 |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | 不可访问 | 不可访问 | 不可访问 |
常用继承方式:
实际开发中优先使用public继承,protected/private继承会限制子类对父类成员的访问,仅在特殊场景使用。
父类名::成员名访问多态是面向对象三大特性之一,指“同一接口,不同实现”,分为静态多态和动态多态。
静态多态在编译阶段确定调用的函数,核心实现方式:函数重载、模板。
class Calculator {
public:
// 重载:int类型加法
int add(int a, int b) {
return a + b;
}
// 重载:float类型加法
float add(float a, float b) {
return a + b;
}
// 重载:三个int类型加法
int add(int a, int b, int c) {
return a + b + c;
}
};
int main() {
Calculator calc;
cout << calc.add(1, 2) << endl; // 调用int版本
cout << calc.add(1.5f, 2.5f) << endl; // 调用float版本
cout << calc.add(1, 2, 3) << endl; // 调用三个int版本
return 0;
}
动态多态在运行阶段确定调用的函数,核心是虚函数(virtual)和纯虚函数,满足三个条件:
// 父类:动物
class Animal {
public:
// 虚函数
virtual void makeSound() {
cout << "动物发出声音" << endl;
}
// 虚析构函数(子类有堆内存时必须声明)
virtual ~Animal() {}
};
// 子类:猫
class Cat : public Animal {
public:
// 重写父类虚函数
void makeSound() override {
cout << "喵喵喵" << endl;
}
};
// 子类:狗
class Dog : public Animal {
public:
// 重写父类虚函数
void makeSound() override {
cout << "汪汪汪" << endl;
}
};
// 测试动态多态
int main() {
Animal* animal1 = new Cat();
Animal* animal2 = new Dog();
// 运行时确定调用子类的函数
animal1->makeSound(); // 输出:喵喵喵
animal2->makeSound(); // 输出:汪汪汪
// 释放内存
delete animal1;
delete animal2;
return 0;
}
纯虚函数是没有实现的虚函数,包含纯虚函数的类为抽象类,不能实例化,仅作为父类被继承:
// 抽象类(包含纯虚函数)
class Shape {
public:
// 纯虚函数(=0表示无实现)
virtual float getArea() = 0;
virtual ~Shape() {}
};
// 子类:矩形(必须实现纯虚函数)
class Rectangle : public Shape {
private:
float m_width;
float m_height;
public:
Rectangle(float w, float h) : m_width(w), m_height(h) {}
// 实现纯虚函数
float getArea() override {
return m_width * m_height;
}
};
int main() {
// Shape s; // 错误:抽象类不能实例化
Shape* rect = new Rectangle(5, 3);
cout << "矩形面积:" << rect->getArea() << endl; // 输出:15
delete rect;
return 0;
}
掌握C++类和面向对象的核心思想,是从“新手”到“进阶”的关键一步。面向对象的封装、继承、多态三大特性,是构建大型、可维护C++程序的基础。